#include "solver.h"
#include "forwardEulerMethod.h"
#include "midpointMethod.h"
#include "rungeKuttaMethod.h"
#include "adaptiveRungeKuttaMethod.h"
#include "improvedVerlet.h"
#include "aproximatedImplicit.h"
#include "implicitSOR.h"
#include "hybridIntegration.h"
#include "implicitJacobi.h"
#include "aproximatedJacobi.h"
#include "state.h"

#define FPS 60

Solver::Solver(){
}

Solver::Solver(std::vector<Fabric *> *_fabrics, std::vector<Object *> *_objects, unsigned int _method,
               double _deltaT, double _gravity, bool _activeCollision, double _collisionDampingFactor, double _collisionDistanceError,
               bool _activeAir, int _airType, double _airPeriod, double _airRepeat, double _airViscosity, Vector3D _airDirection)
                    : fabrics(_fabrics), objects(_objects), testCollision(_activeCollision),
                    collisionDampingFactor(_collisionDampingFactor), collisionDistanceError(_collisionDistanceError)
{
    changeSettings(_method, _deltaT, _gravity, _activeAir, _airType, _airPeriod, _airRepeat, _airViscosity, _airDirection);

    double framesIn = (1.0/_deltaT) / 60.0;

    if (framesIn < 1.0)
    {
        std::cout << "Tera menos de 60 FPS." << std::endl;
        recordFrameInTotal = recordFrameIn = 1;
    }
    else
    {
        recordFrameInTotal = recordFrameIn = floor(framesIn);
    }

//    totalFramesTime = QTime(0,0,0,0);
    firstIteration = true;
    totalMethodTime = 0;
    totalFramesTime = 0;
    totalFrames = 0;
}

Solver::~Solver()
{
    delete method;

    method = 0;

//    delete fabrics;
//    delete objects;
}

//return the vectors of States
std::vector<State> Solver::getStates()
{
    std::vector<State> states;

    for(unsigned int i = 0; i < fabrics->size(); i++)
    {
        states.push_back(fabrics->at(i)->getState());
    }

    return states;
}

//change the methods
void Solver::changeSettings(unsigned int _method, double _deltaT, double _gravity, bool _activeAir, int _airType, double _airPeriod, double _airRepeat, double _airViscosity, Vector3D _airDirection)
{
    /***Colocar em uma função só junto com o que tem no dynamic! */

    std::vector< std::vector<Particle *> > particles;

    std::vector< std::vector<Spring *> > springs;

    for(unsigned int i = 0; i < fabrics->size(); i++)
    {
        particles.push_back(fabrics->at(i)->getParticles());

        springs.push_back(fabrics->at(i)->getSprings());
    }

    switch (_method)
    {
        case FORWARD_EULER:{
            method = new ForwardEulerMethod(_deltaT, _gravity, getStates(), particles, springs, _activeAir, _airType, _airPeriod, _airRepeat, _airViscosity, _airDirection);
            break;
        }
        case MIDPOINT:{
            method = new MidpointMethod(_deltaT, _gravity, getStates(), particles, springs, _activeAir, _airType, _airPeriod, _airRepeat, _airViscosity, _airDirection);
            break;
        }
        case RUNGE_KUTTA:{
            method = new RungeKuttaMethod(_deltaT, _gravity, getStates(), particles, springs, _activeAir, _airType, _airPeriod, _airRepeat, _airViscosity, _airDirection);
            break;
        }
        case ADAPTIVE_RUNGE_KUTTA:{
            method = new AdaptiveRungeKuttaMethod(_deltaT, _gravity, getStates(), particles, springs, _activeAir, _airType, _airPeriod, _airRepeat, _airViscosity, _airDirection);
            break;
        }
        case IMPROVED_VERLET:{
            method = new ImprovedVerlet(_deltaT, _gravity, getStates(), particles, springs, _activeAir, _airType, _airPeriod, _airRepeat, _airViscosity, _airDirection);
            break;
        }
        case APROXIMATED_IMPLICIT:{
            method = new AproximatedImplicit(_deltaT, _gravity, getStates(), particles, springs, _activeAir, _airType, _airPeriod, _airRepeat, _airViscosity, _airDirection);
            break;
        }
        case IMPLICIT_SOR:{
            method = new ImplicitSOR(_deltaT, _gravity, getStates(), particles, springs, _activeAir, _airType, _airPeriod, _airRepeat, _airViscosity, _airDirection);
            break;
        }
        case HYBRID_INTEGRATION:{
            method = new HybridIntegration(_deltaT, _gravity, getStates(), particles, springs, _activeAir, _airType, _airPeriod, _airRepeat, _airViscosity, _airDirection);
            break;
        }
        case IMPLICIT_JACOBI:{
            method = new ImplicitJacobi(_deltaT, _gravity, getStates(), particles, springs, _activeAir, _airType, _airPeriod, _airRepeat, _airViscosity, _airDirection);
            break;
        }
        case APROXIMATED_JACOBI:{
            method = new AproximatedJacobi(_deltaT, _gravity, getStates(), particles, springs, _activeAir, _airType, _airPeriod, _airRepeat, _airViscosity, _airDirection);
            break;
        }
    }
}

void Solver::restart()
{
    for(unsigned int i = 0; i < fabrics->size(); i++)
    {
        method->setState(i, fabrics->at(i)->restart());
    }
    firstIteration = true;
    totalFramesTime = 0;
    totalMethodTime = 0;
    totalFrames = 0;

    method->restart();
}

/*Vector3D Solver::calculateRepulsiveForce(const Vector3D &_position, const Vector3D &_center, double _dist, double _radius )
{
    Vector3D repulsion = (_position - _center).normalize() ;
    //repulsion.normalize();
    double sqrtDist = sqrt(_dist);
    double offset =  _radius - sqrtDist;
    repulsion = repulsion * offset;

    return repulsion;
}*/

